package fetcher

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
)

type Daemon struct {
	cacheDir  string
	remoteURL string
	server    *http.Server
	cache     *Cache
}

func NewDaemon(addr, cacheDir, remote string) *Daemon {
	cache := NewCache(cacheDir)
	mux := http.NewServeMux()
	d := &Daemon{
		cacheDir:  cacheDir,
		remoteURL: remote,
		server:    &http.Server{Addr: addr, Handler: mux},
		cache:     cache,
	}

	mux.HandleFunc("/fetch", d.handleFetch)
	mux.HandleFunc("/prefetch", d.handlePrefetch)
	mux.HandleFunc("/status", d.handleStatus)
	return d
}

func (d *Daemon) Run() error {
	return d.server.ListenAndServe()
}

func (d *Daemon) handleFetch(w http.ResponseWriter, r *http.Request) {
	blockHash := r.URL.Query().Get("hash")
	if blockHash == "" {
		http.Error(w, "missing hash", http.StatusBadRequest)
		return
	}

	// Check cache first
	data, found := d.cache.Get(blockHash)
	if found {
		log.Printf("[cache hit] %s", blockHash)
		w.Write(data)
		return
	}

	log.Printf("[cache miss] fetching %s ...", blockHash)
	data, err := DownloadBlock(d.remoteURL, blockHash)
	if err != nil {
		http.Error(w, fmt.Sprintf("download error: %v", err), http.StatusBadGateway)
		return
	}

	if err := d.cache.Put(blockHash, data); err != nil {
		log.Printf("cache write error: %v", err)
	}

	w.Write(data)
}

func (d *Daemon) handlePrefetch(w http.ResponseWriter, r *http.Request) {
	type Req struct {
		BlockHashes []string `json:"blocks"`
	}
	var req Req
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "invalid request", http.StatusBadRequest)
		return
	}

	for _, h := range req.BlockHashes {
		go func(hash string) {
			if _, ok := d.cache.Get(hash); ok {
				return
			}
			data, err := DownloadBlock(d.remoteURL, hash)
			if err == nil {
				d.cache.Put(hash, data)
			}
		}(h)
	}
	w.Write([]byte("prefetch started"))
}

func (d *Daemon) handleStatus(w http.ResponseWriter, r *http.Request) {
	stats := map[string]interface{}{
		"cache_dir": d.cacheDir,
		"cache_size": d.cache.Size(),
	}
	json.NewEncoder(w).Encode(stats)
}
